A file server is a computer that provides file service relating to the organization of information on storage devices, such as disks. The file server or filer may be embodied as a storage system including a storage operating system that implements a file system to logically organize the information as a hierarchical structure of directories and files on the disks. Each “on-disk” file may be implemented as a set of disk blocks configured to store information, such as text, whereas the directory may be implemented as a specially-formatted file in which information about other files and directories are stored.
As used herein, the term storage operating system generally refers to the computer-executable code operable on a storage system that manages data access and client access requests and may implement file system semantics in implementations involving filers. In this sense, the Data ONTAP™ storage operating system, available from Network Appliance, Inc. of Sunnyvale, Calif., which implements a Write Anywhere File Layout (WAFL™) file system, is an example of such a storage operating system implemented as a microkernel within an overall protocol stack and associated disk storage. The storage operating system can also be implemented as an application program operating over a general-purpose operating system, such as UNIX® or Windows NT®, or as a general-purpose operating system with configurable functionality, which is configured for storage applications as described herein.
A filer's disk storage is typically implemented as one or more storage volumes that comprise physical storage disks, defining an overall logical arrangement of storage space. Currently available filer implementations can serve a large number of discrete volumes (150 or more, for example). A storage volume is “loaded” in the filer by copying the logical organization of the volume's files, data and directories into the filer's memory. Once a volume has been loaded in memory, the volume may be “mounted” by one or more users, applications, devices, etc. permitted to access its contents and navigate its namespace. As used herein, a volume is said to be “in use” when it is loaded in a filer's memory and at least one user, application, etc. has mounted the volume and modified its contents.
Each file and directory stored in a filer is typically identified by a file-handle identifier or “file handle.” A file handle generally includes at least a volume identifier (V), a file identifier (F) and a generation number (G) that collectively describe a specific file or directory in the filer. The volume identifier indicates which storage volume in the filer contains the file or directory. The file identifier identifies the specific file or directory in the volume. For example, if the volume implements an inode-based file system, such as the WAFL™ file system, the file identifier may correspond to an inode number of a file or directory within the volume. The generation number identifies a particular instance of the file or directory in the volume. For instance, if different versions of the same file are stored in the volume, each may be differentiated from the others by its corresponding generation number. In general, the largest generation number for a file or directory corresponds to its most recent version. Of note, file handles may include other information besides a volume identifier, file identifier and generation number. Thus, those skilled in the art will appreciate that a variety of different file-handle implementations are envisioned within the scope of the present invention.
However, for purposes of discussion hereinafter, it will be assumed that file handles are represented as a simple concatenation of a volume identifier, file identifier and generation number, i.e., file handle (FH)=[V][F][G]. For example, a file handle equal to [V=12][F=17][G=6] corresponds to the sixth generation of a file or directory which is assigned a file identifier (or inode number) equal to 17 and is stored in the storage volume whose volume number equals 12. Again, while the notation above will be used for purposes of explanation, those skilled in the art will appreciate that many other file handle formats may be implemented in accordance with the present invention.
A filer may be configured to operate according to a client/server model of information delivery to thereby allow many clients to access files stored on a server, e.g., the filer. In this model, the client may comprise an application, such as a file-system protocol, executing on a computer that “connects” to the filer over a computer network, such as a point-to-point link, shared local area network (LAN), wide area network (WAN), or virtual private network (VPN) implemented over a public network such as the Internet. Communications between the filer and its clients are typically embodied as packets sent over the computer network. Each client may request the services of the filer by issuing file-system protocol messages formatted in accordance with a conventional file-system protocol, such as the Common Internet File System (CIFS) or Network File System (NFS) protocol.
In general, clients are configured to communicate with a file-access protocol engine of the filer using a stateful or stateless file-system protocol. A stateful protocol, such as the CIFS protocol, is a connection-oriented protocol that requires the filer, e.g., the file-access protocol engine, and the client to establish a communication session (or “virtual circuit”) through which they exchange information. Each communication session is then associated with session-specific “state” information, which may include, inter alia, authentication information, session identifiers, file-handle identifiers, and so forth. In the event the session is lost or interrupted, its state information may be used to reestablish the session without having to re-authenticate the client or renegotiate many of the session parameters. Upon re-establishing the stateful-protocol session, the filer typically invalidates the client's outstanding file handles and issues a new set of file handles to the client. Thus, any client requests that were lost as a result of the session failure can be “replayed” by the client using the new set of file handles.
In contrast, a stateless protocol, such as the NFS protocol, does not require establishment of a formal communication session. Instead, each client request in a stateless protocol is authenticated and processed by the filer on a per-request basis rather than a per-session basis. That is, the validity of a client request in a stateless protocol is not bound to the duration of any specific communication session. Thus, unlike file handles used in stateful protocols, file handles in stateless protocols may remain valid even after the filer has been temporarily shutdown or disabled.
A non-volatile random access memory (NVRAM) is often employed in a filer's architecture as a backup memory that ensures the filer does not “lose” received client information, e.g., CIFS and NFS requests, in the event of a system shutdown or other unforeseen problem. The NVRAM is typically a large-volume solid-state memory array (RAM) having either a back-up battery, or other built-in last-state-retention capabilities (e.g. a FLASH memory), that holds the last state of the memory in the event of any power loss to the array. Therefore, even if a client request stored in the filer's “in-core” memory is lost or erased, e.g., due to a temporary power outage, it still may be recovered from the filer's NVRAM.
However, data recovery problems can arise when the contents of the filer's “in-core” memory is lost or corrupted and, in addition, the filer's backup NVRAM fails. As used herein, an NVRAM “fails” when it is unable to function in its normal capacity for storing and retaining information. Such a failure may be due to electrical or mechanical problems within the NVRAM, loss of power supplied to the NVRAM, failure of a memory controller coupled to the NVRAM, etc. After an NVRAM failure, the NVRAM's lost data may be not be recoverable in the filer if the data was also erased, overwritten or corrupted in the filer's in-core memory, e.g., due to a reboot operation involving the filer. In such a situation, the filer can only recover the lost data by re-retrieving the data from its original source or a suitable substitute.
For example, suppose an unprocessed client request is held in the NVRAM when it fails. Further suppose the request is not resident in the filer's in-core memory after the NVRAM fails and the filer's operation is restored. If the request was issued in accordance with a stateful protocol, such as the CIFS protocol, the requesting client may reestablish its communication session with the filer and replay the lost request using a newly issued file handle. However, if the client's request was issued according to a stateless protocol, such as the NFS protocol, the requesting client may not be notified of the NVRAM failure. Worse yet, the “stateless” client may continue to send the filer requests for files and directories that were lost or damaged by the NVRAM's failure. For this reason, the filer may be configured to identify when a received client request has become invalid as a result of an NVRAM failure.
A previous approach for detecting invalid client requests utilizes “internal” and “external” file handle representations for each file and directory stored in the filer. As defined herein, an external file handle is a file handle by which a client identifies a file or directory stored in the filer. In contrast, an internal file handle is a file handle used within the filer's storage operating system (and, in particular, the file system) to identify a file or directory stored in the filer. Prior filer implementations have employed two different types of internal file handles: “on-disk” and in-core file handles. An on-disk file handle uniquely identifies a file or directory stored on one of the filer's storage mediums, such as a storage disk. An in-core file handle uniquely identifies a file or directory stored in the filer's volatile (in-core) memory. Notably, a file's on-disk, in-core and external file handles need not be identical, and, in fact, are different in many cases.
In prior filer implementations, on-disk and in-core file handles for the same file or directory may differ only in the value of their generation numbers. That is, the generation number of a file's in-core file handle may be greater than the generation number of its on-disk file handle by an amount equal to an NVRAM-failure (“nvfail”) count value. Each volume in the filer is associated with a respective nvfail count value that stores the number of times the filer's NVRAM failed while the volume was in use. Typically, each volume's nvfail count value is cumulative and persistent for the “life” of the volume. For example, the first generation of a file identifier 17 located in volume 12 has an on-disk file handle equal to [V=12][F=17][G=1]. If the volume 12 has an associated nvfail count equal to 2, then when the file is copied into the filer's in-core memory, the file system assigns the file an in-core file handle equal to [V=12][F=17][G=3]. In operation, the in-core file handles for files and directories are exported to clients that request access to those files and directories. Accordingly, a file's external file handle typically coincides with its in-core file-handle representation.
In response to receiving a client file access request, the filer determines the validity of the external file handle associated with the request to ensure that the file handle has not become invalid (“stale”) as a result of an NVRAM failure in the filer. To that end, the file system decrements the received file handle's generation number by the current nvfail count value associated with the volume containing the file handle's referenced file or directory. The resultant generation number is then compared with the generation number contained in the requested file or directory's on-disk file handle. If the two generation numbers are equal, the file system concludes the external file handle is valid.
For example, suppose the external file handle [V=12][F=17][G=3] is received at the filer to reference a file abc. Also, assume the referenced file abc has an on-disk internal file handle equal to [V=12][F=17][G=1]. Notably, the volume 12's nvfail count value equaled two when the external file handle was generated. Next, further assume the filer's NVRAM failed some time after the external file handle was issued, and the volume 12's nvfail count has been incremented to currently equal three. In this case, the file system can test the validity of the external file handle by subtracting volume 12's current nvfail count value (3) from the external file handle's generation number (G=3) and comparing the resultant generation number (G=0) to the generation number of the file abc's on-disk file handle (G=1). Since the comparison indicates the generation numbers are not equal (0≠1), the file system concludes the external file handle it received is invalid.
Although the above-mentioned technique is effective for identifying invalid file handles received at the filer, it adds complexity to file management within the filer. Specifically, every time a file (or directory) is transferred between the filer's storage disks and memory, the file's generation number must be adjusted by the nvfail count value of its containing volume. The computation required to convert between in-core and on-disk generation numbers may consume a substantial amount of time and processing resources in the filer. This is especially true when the file system manipulates a large number of files and directories, e.g., in a database system, that are repeatedly updated and modified. Furthermore, the generation number adjustments often result in the file system adding and subtracting a value of zero, since filer volumes often have nvfail count values equal to zero. In this case, the on-disk and in-core file handles are equal, so the adjustment operation performed within the file system is essentially unnecessary. In addition, the process of converting between on-disk and in-core generation numbers permeates all file-management software in the filer's code base (including the file system), thereby increasing the possibility of hard-to-detect errors due to improper conversions.
As noted, prior filer implementations require a filer to verify every external file handle it receives by modifying the generation number of the received file handle and comparing the resultant generation number to an “expected” generation number. This file-handle validation procedure is applied regardless of whether the external file handle is formatted according to a stateful or stateless file-system protocol. Thus, the filer may unnecessarily consume time and processing resources testing the validity of external file handles used in a stateful protocol, such as the CIFS protocol. Further, when a large number of external file handles are received at the filer, the process of validating the large number of file handles may consume excessive time and filer resources.
It is therefore desirable to reduce the amount of time and processing resources, such as central processor unit (CPU) bandwidth and memory usage, consumed by a filer that generates and validates external file handles that may become invalid after an NVRAM failure in the filer. It is also desirable to simplify the manner in which the filer identifies files and directories stored in its storage disks and in its in-core memory.